随着时代的发展,用户对应用体验的要求愈加苛刻,我们虽然可以通过各种优化手段来减少页面加载时间,但当用户处于移动状态,潜在的网络切换很可能导致短暂的离线,如果用户在此时进行事务处理,那么此刻应用的不可用很可能导致用户流失。也许正是由于 Web 应用在离线处理上的弱势,才最终导致其地位在移动时代不如原生应用这一局面。
那又该如何破局,将本机原生应用所具有的离线处理能力植入到 Web 应用中去呢?接下来我们要讨论的 Service Worker 便可解决这一难题。
# Service Worker 与 Web Worker
- 首次看到
Service Worker,我想大家可能会跟我一样都有这东西跟 Web Worker 有什么联系之类的疑问,带着这个疑问让我们来梳理下两者的差异。 Web Worker是现代浏览器提供的一个JavaScript多线程解决方案,我们可以将一些复杂、耗时的运算交给 Web Worker 执行以达到释放主线程的目的;Service Worker则是建立在Web Worker之上,旨在通过请求代理、本地缓存、后台同步等机制来提供离线处理能力。两者的主要异同点如下:
相同点
- 都独立于主线程,以单独线程的形式运行。
- 都不能直接访问并操作
DOM、window对象。 - 都是通过
postMessage接口与主线程进行交互。
不同点
Service Worker内部大部分为基于Promise的异步操作。Service Worker必须运行在HTTPS环境下以避免中间人攻击。Service Worker的生命周期完全独立于网页,且可在不用时被中止、在下次有需要时重启。
# 生命周期
上一节我们对
Service Worker及Web Worker进行了简单对比,下面我们将深入了解 Service Worker 的生命周期。
# 注册
要使用 Service Worker,需通过以下方式在页面中对其进行注册:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('./sw.js', { scope: './' }).then(function(registration) {
// do domething...
}).catch(function(err) {
// do domething...
});
});
}
@前端进阶之旅: 代码已经复制到剪贴板
示例首先检测
Service Worker是否可用,如果可用,则在页面加载完后注册位于./sw.js的Service Worker。代码非常简单,但需注意以下两点:
- 注册成功仅仅表明指定脚本已成功解析,并不意味着 Service Worker 已经安装或处于激活状态。
register方法中的 scope 参数指定了 Service Worker 可接收 fetch 事件的作用域,比如 scope 的值为 /mobile,那么 Service Worker 便只能接收 path 以/mobile开头的 fetch 事件,默认值为sw.js所在路径。
# 安装
注册完成后,浏览器便会立即尝试安装并进入安装状态,此时将触发
Service Worker的install事件,在该事件中我们经常对静态资源进行缓存处理,比如:
